/*!
\copyright  Copyright (c) 2020-2022 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief      Implementation of common LE Audio specifc testing functions.
*/

#include "le_audio_test.h"
#include <logging.h>
#include <macros.h>

#include "focus_audio_source.h"
#include "focus_voice_source.h"
#include "le_advertising_manager_select_extended.h"
#ifdef INCLUDE_LE_AUDIO_BROADCAST
#include "le_broadcast_manager.h"
#endif
#ifdef INCLUDE_TWS
#include "tws_topology.h"
#endif
#include "volume_messages.h"
#include "volume_service.h"

#include "connection_manager_list.h"
#include "bt_device.h"

#include "mirror_profile.h"


#ifdef GC_SECTIONS
/* Move all functions in KEEP_PM section to ensure they are not removed during
 * garbage collection */
#pragma unitcodesection KEEP_PM
#endif

#if defined(INCLUDE_LE_AUDIO_BROADCAST) || defined(INCLUDE_LE_AUDIO_UNICAST)
static void leAudioTest_taskHandler(Task task, MessageId id, Message msg);

static TaskData le_audio_test_taskdata = {leAudioTest_taskHandler};
#endif

bool leAudioTest_IsExtendedAdvertisingActive(void)
{
#ifndef INCLUDE_LEGACY_LE_ADVERTISING_MANAGER
    // needs revisiting, maybe remove
    return FALSE;
#else
    return LeAdvertisingManager_IsExtendedAdvertisingActive();
#endif
}

bool leAudioTest_IsBroadcastReceiveActive(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    return LeBroadcastManager_IsBroadcastReceiveActive();
#else
    return FALSE;
#endif
}

bool leAudioTest_IsAnyBroadcastSourceSyncedToPa(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    return LeBroadcastManager_IsAnySourceSyncedToPa();
#else
    return FALSE;
#endif
}

bool leAudioTest_IsAnyBroadcastSourceSyncedToBis(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    return LeBroadcastManager_IsAnySourceSyncedToBis();
#else
    return FALSE;
#endif
}

static bool leAudioTest_SetVolumeForLeaAudioSource(audio_source_t source, uint8 volume)
{
#ifdef INCLUDE_TWS
    if (TwsTopology_IsSecondary())
    {
        DEBUG_LOG_ALWAYS("This Test API should never be called on the secondary Earbud");
        return FALSE;
    }
#endif
    audio_source_t focused_source = audio_source_none;
    if (!Focus_GetAudioSourceForContext(&focused_source))
    {
        DEBUG_LOG_ALWAYS("no focused audio source");
        return FALSE;
    }
    if (focused_source != source)
    {
        DEBUG_LOG_ALWAYS("focused audio source is not enum:audio_source_t:%d", source);
        return FALSE;
    }
    Volume_SendAudioSourceVolumeUpdateRequest(focused_source, event_origin_local, volume);
    return TRUE;
}

bool leAudioTest_SetVolumeForBroadcast(uint8 volume)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SetVolumeForBroadcast %d", volume);
    return leAudioTest_SetVolumeForLeaAudioSource(audio_source_le_audio_broadcast, volume);
}

bool leAudioTest_SetVolumeForUnicastMusic(uint8 volume)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SetVolumeForUnicast %d", volume);
    return leAudioTest_SetVolumeForLeaAudioSource(audio_source_le_audio_unicast, volume);
}

static bool leAudioTest_SetMuteForLeaAudioSource(audio_source_t source, bool mute_state)
{
#ifdef INCLUDE_TWS
    if (TwsTopology_IsSecondary())
    {
        DEBUG_LOG_ALWAYS("This Test API should never be called on the secondary Earbud");
        return FALSE;
    }
#endif
    audio_source_t focused_source = audio_source_none;
    if (!Focus_GetAudioSourceForContext(&focused_source))
    {
        DEBUG_LOG_ALWAYS("no focused audio source");
        return FALSE;
    }
    if (focused_source != source)
    {
        DEBUG_LOG_ALWAYS("focused audio source is not enum:audio_source_t:%d", source);
        return FALSE;
    }
    Volume_SendAudioSourceMuteRequest(focused_source, event_origin_local, mute_state);
    return TRUE;
}

bool leAudioTest_SetMuteForBroadcast(bool mute_state)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SetMuteForBroadcast %d", mute_state);
    return leAudioTest_SetMuteForLeaAudioSource(audio_source_le_audio_broadcast, mute_state);
}

bool leAudioTest_SetMuteForUnicastMusic(bool mute_state)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SetMuteForUnicastMusic %d", mute_state);
    return leAudioTest_SetMuteForLeaAudioSource(audio_source_le_audio_unicast, mute_state);
}

bool leAudioTest_PauseBroadcast(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    DEBUG_LOG_ALWAYS("leAudioTest_PauseBroadcast");
    LeBroadcastManager_Pause(&le_audio_test_taskdata);
    return TRUE;
#else
    return FALSE;
#endif
}

bool leAudioTest_ResumeBroadcast(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    DEBUG_LOG_ALWAYS("leAudioTest_ResumeBroadcast");
    LeBroadcastManager_Resume(&le_audio_test_taskdata);
    return TRUE;
#else
    return FALSE;
#endif
}

bool leAudioTest_IsBroadcastPaused(void)
{
#ifdef INCLUDE_LE_AUDIO_BROADCAST
    bool paused = LeBroadcastManager_IsPaused();
    DEBUG_LOG_ALWAYS("leAudioTest_IsBroadcastPaused %d", paused);
    return paused;
#else
    return TRUE;
#endif
}

bool leAudioTest_SetVolumeForUnicastVoice(uint8 volume)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SetVolumeForUnicastVoice %d", volume);
#ifdef INCLUDE_TWS
    if (TwsTopology_IsSecondary())
    {
        DEBUG_LOG_ALWAYS("This Test API should never be called on the secondary Earbud");
        return FALSE;
    }
#endif
    voice_source_t focused_source = voice_source_none;
    if (!Focus_GetVoiceSourceForContext(ui_provider_telephony, &focused_source))
    {
        DEBUG_LOG_ALWAYS("no focused voice source");
        return FALSE;
    }
    if (focused_source != voice_source_le_audio_unicast)
    {
        DEBUG_LOG_ALWAYS("focused audio source is not enum:voice_source_t:%d", voice_source_le_audio_unicast);
        return FALSE;
    }
    Volume_SendVoiceSourceVolumeUpdateRequest(focused_source, event_origin_local, volume);
    return TRUE;
}

bool leAudioTest_SetMuteForUnicastVoice(bool mute_state)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SetMuteForUnicastVoice %d", mute_state);
#ifdef INCLUDE_TWS
    if (TwsTopology_IsSecondary())
    {
        DEBUG_LOG_ALWAYS("This Test API should never be called on the secondary Earbud");
        return FALSE;
    }
#endif
    voice_source_t focused_source = voice_source_none;
    if (!Focus_GetVoiceSourceForContext(ui_provider_telephony, &focused_source))
    {
        DEBUG_LOG_ALWAYS("no focused voice source");
        return FALSE;
    }
    if (focused_source != voice_source_le_audio_unicast)
    {
        DEBUG_LOG_ALWAYS("focused audio source is not enum:voice_source_t:%d", voice_source_le_audio_unicast);
        return FALSE;
    }
    Volume_SendVoiceSourceMuteRequest(focused_source, event_origin_local, mute_state);
    return TRUE;
}

int leAudioTest_GetVolumeForBroadcast(void)
{
    int volume = AudioSources_GetVolume(audio_source_le_audio_broadcast).value;

    DEBUG_LOG_ALWAYS("leAudioTest_GetVolumeForBroadcast Volume:%d", volume);

    return volume;
}

int leAudioTest_GetVolumeForUnicastMusic(void)
{
    int volume = AudioSources_GetVolume(audio_source_le_audio_unicast).value;

    DEBUG_LOG_ALWAYS("leAudioTest_GetVolumeForUnicastMusic Volume:%d", volume);

    return volume;
}

int leAudioTest_GetVolumeForUnicastVoice(void)
{
    int volume = VoiceSources_GetVolume(voice_source_le_audio_unicast).value;

    DEBUG_LOG_ALWAYS("leAudioTest_GetVolumeForUnicastVoice Volume:%d", volume);

    return volume;
}

#ifdef INCLUDE_LE_AUDIO_UNICAST

/*! Increment/decrement the volume of unicast sink */
static void leAudioTest_UnicastSinkVolumeChange(source_type_t source_type, bool is_vol_down)
{
    int step_size;
    bool is_audio_src = source_type == source_type_audio;

    step_size = VolumeService_GetUiStepSize(is_audio_src ? AudioSources_GetVolume(audio_source_le_audio_unicast).config :
                                                           VoiceSources_GetVolume(voice_source_le_audio_unicast).config);

    if (is_vol_down)
    {
        step_size *= -1;
    }

    if (is_audio_src)
    {
        VolumeService_ChangeAudioSourceVolume(audio_source_le_audio_unicast, step_size);
    }
    else
    {
        VolumeService_ChangeVoiceSourceVolume(voice_source_le_audio_unicast, step_size);
    }
}

void leAudioTest_UnicastAudioSinkVolumeUp(void)
{
    DEBUG_LOG_ALWAYS("leAudioTest_UnicastAudioSinkVolumeUp");

    leAudioTest_UnicastSinkVolumeChange(source_type_audio, FALSE);
}

void leAudioTest_UnicastAudioSinkVolumeDown(void)
{
    DEBUG_LOG_ALWAYS("leAudioTest_UnicastAudioSinkVolumeDown");

    leAudioTest_UnicastSinkVolumeChange(source_type_audio, TRUE);
}

void leAudioTest_UnicastVoiceSinkVolumeUp(void)
{
    DEBUG_LOG_ALWAYS("leAudioTest_UnicastVoiceSinkVolumeUp");

    leAudioTest_UnicastSinkVolumeChange(source_type_voice, FALSE);
}

void leAudioTest_UnicastVoiceSinkVolumeDown(void)
{
    DEBUG_LOG_ALWAYS("leAudioTest_UnicastVoiceSinkVolumeDown");

    leAudioTest_UnicastSinkVolumeChange(source_type_voice, TRUE);
}

void leAudioTest_SendAseReleaseRequest(gatt_cid_t cid, uint8 ase_id)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SendAseReleaseRequest");
    LeUnicastManager_SendAseReleaseRequest(cid, ase_id);
}

void leAudioTest_SendAseReleasedRequest(gatt_cid_t cid, uint8 ase_id)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SendAseReleasedRequest");
    LeUnicastManager_SendAseReleasedRequest(cid, ase_id);
}

void leAudioTest_SendAseDisableRequest(gatt_cid_t cid, uint8 ase_id)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SendAseDisableRequest");
    LeUnicastManager_SendAseDisableRequest(cid, ase_id);
}

void leAudioTest_SendAseAseConfigureCodecReq(gatt_cid_t cid, uint8 ase_id, le_um_codec_config_set_t config_set)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SendAseAseConfigureCodecReq");
    LeUnicastManager_SendAseConfigureCodecReq(cid, ase_id, config_set);
}

void leAudioTest_SendAseMetadataUpdateReq(gatt_cid_t cid, uint8 ase_id)
{
    DEBUG_LOG_ALWAYS("leAudioTest_SendAseMetadataUpdateReq");
    LeUnicastManager_SendAseMetadataUpdate(cid, ase_id);
}

bool leAudioTest_IsCcpConnected(void)
{
    bool is_connected = CallClientControl_IsCcpConnected();
    DEBUG_LOG_ALWAYS("leAudioTest_IsCcpConnected %d", is_connected);

    return is_connected;
}

bool appTestIsLeaUnicastStreaming(void)
{
    bool is_streaming = LeUnicastManager_IsStreamingActive();

    DEBUG_LOG_ALWAYS("appTestIsLeaUnicastStreaming %d", is_streaming);
    return is_streaming;
}

uint16 appTestLeaUnicastGetAudioContext(void)
{
    uint16 audio_context = LeUnicastManager_GetUnicastAudioContext();

    DEBUG_LOG_ALWAYS("appTestIsLeaUnicastGetAudioContext audio_context=enum:audio_context_t:%d", audio_context);
    return audio_context;
}

bool leAudioTest_ReadUnicastCisLinkQuality(void)
{
    hci_connection_handle_t handles[2] = {LE_INVALID_CIS_HANDLE};

#ifdef ENABLE_LEA_CIS_DELEGATION
    MirrorProfile_GetCisHandle(&handles[0], &handles[1]);

    DEBUG_LOG_ALWAYS("leAudioTest_ReadUnicastCisLinkQuality own-handle: 0x%x, peer-handle: 0x%x", handles[0], handles[1]);
#else
    DEBUG_LOG_ALWAYS("leAudioTest_ReadUnicastCisLinkQuality total handles: %d, handle-1: 0x%x, handle-2: 0x%x",
                     LeUnicastManager_GetCisHandles(ARRAY_DIM(handles), handles), handles[0], handles[1]);
#endif

    if (handles[0] != LE_INVALID_CIS_HANDLE)
    {
        CmIsocReadIsoLinkQualityReq(&le_audio_test_taskdata, handles[0]);
    }

    if (handles[1] != LE_INVALID_CIS_HANDLE)
    {
        CmIsocReadIsoLinkQualityReq(&le_audio_test_taskdata, handles[1]);
    }

    return handles[0] != LE_INVALID_CIS_HANDLE || handles[1] != LE_INVALID_CIS_HANDLE;
}

#endif /* INCLUDE_LE_AUDIO_UNICAST */

bool leAudioTest_AnyHandsetConnectedBothBredrAndLe(void)
{
    cm_connection_t *le_connection;

    le_connection = ConManagerFindFirstActiveLink(cm_transport_ble);
    while (le_connection)
    {
        const tp_bdaddr *address = ConManagerGetConnectionTpAddr(le_connection);
        if (address)
        {
            tp_bdaddr le_address = {0};

            if (   le_address.taddr.type != TYPED_BDADDR_RANDOM
                || !VmGetPublicAddress(address, &le_address))
            {
                le_address = *address;
            }

            if (le_address.taddr.type == TYPED_BDADDR_PUBLIC)
            {
                tp_bdaddr bredr_address = le_address;
                bredr_address.transport = TRANSPORT_BREDR_ACL;
                if (ConManagerFindConnectionFromBdAddr(&bredr_address))
                {
                    DEBUG_LOG_ALWAYS("leAudioTest_AnyHandsetConnectedBothBredrAndLe. Found device with LE and BREDR addr:(0x%6lx)",
                                le_address.taddr.addr.lap);
                    return TRUE;
                }
            }
        }
        le_connection = ConManagerFindNextActiveLink(le_connection, cm_transport_ble);
    }

    DEBUG_LOG_ALWAYS("leAudioTest_AnyHandsetConnectedBothBredrAndLe. No devices.");

    return FALSE;
}

#if defined(INCLUDE_LE_AUDIO_BROADCAST) || defined(INCLUDE_LE_AUDIO_UNICAST)
static void leAudioTest_taskHandler(Task task, MessageId id, Message msg)
{
    UNUSED(task);

#ifdef INCLUDE_LE_AUDIO_UNICAST

    if (id == CM_PRIM && (*(CsrBtCmPrim *) msg) == CM_ISOC_READ_ISO_LINK_QUALITY_CFM)
    {
        CmIsocReadIsoLinkQualityCfm *cfm = (CmIsocReadIsoLinkQualityCfm *) msg;

        DEBUG_LOG_ALWAYS("leAudioTest_taskHandler::CM_ISOC_READ_ISO_LINK_QUALITY_CFM handle: 0x%x, result: {code: 0x%x, supplier: 0x%x}",
                         cfm->handle, cfm->resultCode, cfm->resultSupplier);
        DEBUG_LOG_ALWAYS("     tx_unacked_packets: 0x%x, tx_flushed_packets: 0x%x, tx_last_subevent_packets: 0x%x, retransmitted_packets: 0x%x",
                         cfm->tx_unacked_packets, cfm->tx_flushed_packets, cfm->tx_last_subevent_packets, cfm->retransmitted_packets);
        DEBUG_LOG_ALWAYS("     crc_error_packets: 0x%x, rx_unreceived_packets: 0x%x, duplicate_packets: 0x%x",
                         cfm->crc_error_packets, cfm->rx_unreceived_packets, cfm->duplicate_packets);
    }

#else /* INCLUDE_LE_AUDIO_UNICAST */

    UNUSED(id);
    UNUSED(msg);

#endif /* INCLUDE_LE_AUDIO_UNICAST */
}
#endif /* defined(INCLUDE_LE_AUDIO_BROADCAST) || defined(INCLUDE_LE_AUDIO_UNICAST) */

